using System;
using System.Collections.Generic;
using Core;
using UnityEngine;
using Component = Core.Component;

namespace g_gongjianta
{
    public static class UnitExt
    {
        public static UnitSystem Unit(this AbstractGameContext g) => g.Get<UnitSystem>();
    }

    public partial class UnitSystem : Component, IAwake, IGameObject
    {
        private readonly IReadOnlyList<Unit> empty = new List<Unit>();

        private readonly Dictionary<long, Unit> dict = new();
        private readonly Dictionary<Type, List<Unit>> typeDict = new();

        public GameObject gameObject { get; private set; }
        public Transform transform { get; private set; }

        public void Awake()
        {
            gameObject = Utils.Trans.Find(S.gameObject, "units", true);
            transform = gameObject.transform;

            dict.Clear();
            typeDict.Clear();

            // InitCreateTypes();
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            dict.Clear();
            typeDict.Clear();
        }

        public void Add(Unit unit)
        {
            if (!dict.TryAdd(unit.InstanceId, unit))
            {
                return;
            }

            var t = unit.GetType();
            if (typeDict.ContainsKey(t) == false)
            {
                typeDict[t] = new List<Unit>();
            }

            var elements = typeDict[t];
            elements.Add(unit);
        }

        public void Remove(Unit unit)
        {
            if (unit == null) return;
            var id = unit.InstanceId;
            var t = unit.GetType();
            if (dict.ContainsKey(id) == false)
            {
                return;
            }

            dict.Remove(id);
            if (typeDict.TryGetValue(t, out var elements))
            {
                elements.Remove(unit);
            }
        }

        public T Get<T>() where T : Unit
        {
            var t = typeof(T);
            return Get(t) as T;
        }

        public Unit Get(Type t)
        {
            if (typeDict.TryGetValue(t, out var elements) == false)
            {
                return null;
            }

            if (elements.Count <= 0)
            {
                return null;
            }

            return elements[0];
        }

        public int Count<T>()
        {
            return Count(typeof(T));
        }

        public int Count(Type t)
        {
            if (typeDict.TryGetValue(t, out var value) == false)
            {
                return 0;
            }

            return value.Count;
        }

        public IReadOnlyList<Unit> GetUnits<T>()
        {
            var t = typeof(T);
            return GetUnits(t);
        }

        public IReadOnlyList<Unit> GetUnits(Type t)
        {
            if (typeDict.TryGetValue(t, out var units) == false)
            {
                return empty;
            }

            return units;
        }

        public void GetUnits<T>(List<T> list)
        {
            list.Clear();

            var t = typeof(T);
            if (typeDict.TryGetValue(t, out var elements) == false)
            {
                return;
            }

            for (var i = 0; i < elements.Count; i++)
            {
                if (elements[i] is not T ele)
                {
                    continue;
                }

                list.Add(ele);
            }
        }

        // public void GetUnits<T>(List<T> lst, Predicate<T> func)
        //     where T : Unit
        // {
        //     lst.Clear();
        //
        //     var t = typeof(T);
        //     if (typeDict.ContainsKey(t) == false)
        //     {
        //         return;
        //     }
        //
        //     var elements = typeDict[t];
        //     for (int i = 0; i < elements.Count; i++)
        //     {
        //         if (elements[i] is not T ele)
        //         {
        //             continue;
        //         }
        //         if (func == null || func(ele))
        //         {
        //             lst.Add(ele);
        //         }
        //     }
        // }
        //
        // public T Find<T>(Predicate<T> func)
        //     where T : Unit
        // {
        //     var t = typeof(T);
        //
        //     var elements = GetUnits(t);
        //     if (elements.Count == 0)
        //     {
        //         return default;
        //     }
        //
        //     T element = default;
        //     for (int i = 0; i < elements.Count; i++)
        //     {
        //         var ele = elements[i] as T;
        //         if (func(ele))
        //         {
        //             element = ele;
        //             break;
        //         }
        //     }
        //
        //     return element;
        // }
        //
        // public T FindNearest<T>(IReadOnlyList<T> lst, Vector2 tgt_pos)
        //     where T : Unit
        // {
        //     T t = default;
        //
        //     var min_dis = float.MaxValue;
        //
        //     for (int i = 0; i < lst.Count; i++)
        //     {
        //         var ele = lst[i];
        //         var pos = ele.Pos;
        //         var dis = (tgt_pos - pos).sqrMagnitude;
        //         if (dis < min_dis)
        //         {
        //             t = ele;
        //             min_dis = dis;
        //         }
        //     }
        //
        //     return t;
        // }

        public T FindNearest<T>(Vector2 tgt_pos)
            where T : Unit
        {
            var list = GetUnits<T>();

            T t = null;

            var minDis = float.MaxValue;

            for (var i = 0; i < list.Count; i++)
            {
                if (list[i] is not T ele) continue;

                var pos = ele.Pos;
                var dis = Utils.Math.GetAD(tgt_pos, pos);
                if (dis < minDis)
                {
                    t = ele;
                    minDis = dis;
                }
            }

            return t;
        }
    }
}